package com.thiswhyme.openfeign.gson;

import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import com.thiswhyme.openfeign.gson.adapter.*;
import com.thiswhyme.openfeign.gson.config.GsonProperties;
import com.thiswhyme.openfeign.gson.strategy.IgnoreAnnotationExclusionStrategy;
import org.apache.commons.lang3.reflect.TypeUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;
import springfox.documentation.spring.web.json.Json;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * @description:
 * @author: thiswhyme
 * @date: 2020/8/20
 */
public class ThiswhymeGsonBuilder {

    public static GsonBuilder createCommonBuilder(GsonProperties gsonProperties) {
        GsonBuilder gsonBuilder = new GsonBuilder()
                .setLongSerializationPolicy(LongSerializationPolicy.STRING)
                .serializeNulls()
                .setExclusionStrategies(new IgnoreAnnotationExclusionStrategy())
                .registerTypeAdapter(Json.class, new SpringfoxJsonToGsonAdapter())
                .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer())
                .registerTypeAdapter(LocalDate.class, new LocalDateSerializer())
                .registerTypeAdapter(new TypeToken<Map<String, Object>>() {}.getType(), new MapTypeAdapter());
//                .registerTypeAdapterFactory(new EnumAdapterFactory());


        if(null != gsonProperties) {
            gsonBuilder.setDateFormat(gsonProperties.getDatePattern());

            List<Class<ExclusionStrategy>> exclusionStrategies = gsonProperties.getExclusionStrategies();
            if (!CollectionUtils.isEmpty(exclusionStrategies)) {
                for (Class<ExclusionStrategy> bean : exclusionStrategies) {
                    ExclusionStrategy strategy = BeanUtils.instantiateClass(bean);
                    gsonBuilder.setExclusionStrategies(strategy);
                }
            }
            List<Class<GsonBizAdapter>> adapterClazzList = gsonProperties.getBizAdapters();
            if (!CollectionUtils.isEmpty(adapterClazzList)) {
                adapterClazzList.forEach(adapterClazz -> {
                    GsonBizAdapter adapter = BeanUtils.instantiateClass(adapterClazz);
                    if(null != adapter.getType()) {
                        gsonBuilder.registerTypeAdapter(adapter.getType(), adapter);
                    } else if (null != adapter.getClazz()) {
                        gsonBuilder.registerTypeAdapter(adapter.getClazz(), adapter);
                    } else {
                        throw new RuntimeException("请提供序列化器对应的目标对象类型");
                    }
                });
            }
        }
        return gsonBuilder;
    }

    public static Gson fetchCommonGson(GsonProperties gsonProperties) {
        return ThiswhymeGsonBuilder.createCommonBuilder(gsonProperties).create();
    }

    public static final <T> String toJson(T object) {
        try {
            return ThiswhymeGsonBuilder.fetchCommonGson(null).toJson(object);
        } catch (JsonSyntaxException e) {
            throw new RuntimeException("给定对象按默认规则转换json字符串失败", e);
        }
    }

    public static final <T extends Serializable> T fromJson(Class<T> clazz, String json) {
        try {
            return ThiswhymeGsonBuilder.fetchCommonGson(null).fromJson(json, TypeUtils.parameterize(clazz));
        } catch (JsonSyntaxException e) {
            throw new RuntimeException("json格式和给定格式不一致，无法解析", e);
        }
    }

    public static final <T extends Serializable> T fromJson(ParameterizedType type, String json) {
        try {
            return ThiswhymeGsonBuilder.fetchCommonGson(null).fromJson(json, type);
        } catch (JsonSyntaxException e) {
            throw new RuntimeException("json格式和给定格式不一致，无法解析", e);
        }
    }

    public static final <T extends Serializable> T fromJson(Type type, String json) {
        try {
            return ThiswhymeGsonBuilder.fetchCommonGson(null).fromJson(json, type);
        } catch (JsonSyntaxException e) {
            throw new RuntimeException("json格式和给定格式不一致，无法解析", e);
        }
    }
}
